package com.hks.quart.utils;

import okhttp3.*;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 单例模式实现
 */
public class OkHttpUtils {
    private static final Logger log = LoggerFactory.getLogger(OkHttpUtils.class);
    private volatile static OkHttpUtils okHttpUtils;
    private static OkHttpClient okHttpClient;

    static {
        okHttpClient = new OkHttpClient.Builder().retryOnConnectionFailure(true)//是否开启缓存
                .connectionPool(new ConnectionPool(50, 5, TimeUnit.MINUTES)).connectTimeout(30L, TimeUnit.SECONDS)
                .readTimeout(300L, TimeUnit.SECONDS).writeTimeout(30L, TimeUnit.SECONDS).build();
    }

    private OkHttpUtils() {
    }

    public static OkHttpUtils getInstance() {
        if (okHttpUtils == null) {
            // 双重检测，保证线程安全
            synchronized (OkHttpUtils.class) {
                if (okHttpUtils == null) {
                    okHttpUtils = new OkHttpUtils();
                }
            }
        }
        return okHttpUtils;
    }

    /**
     * 根据map获取get请求参数
     *
     * @param params 请求参数集
     * @return
     */
    public String getUrlStr(String url, Map<String, String> params) {
        if (!params.isEmpty()) {
            url = String.format("%s?%s", url, getParamStr(params));
        }
        return url;
    }

    public String getParamStr(Map<String, String> params) {
        StringBuffer buffer = new StringBuffer();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            buffer.append(String.format("%s=%s&", entry.getKey(), entry.getValue()));
        }
        if (buffer.length() > 0) {
            buffer.deleteCharAt(buffer.length() - 1);
        }
        return buffer.toString();
    }

    /**
     * 调用OkHttp的newCall方法（同步）
     *
     * @param request 请求体
     * @return
     */
    private String execNewCall(Request request) {
        log.debug("{} {}", request.method(), request.url());
        for (Map.Entry<String, List<String>> header : request.headers().toMultimap().entrySet()) {
            log.debug("{}: {}", header.getKey(), header.getValue());
        }
        Response response = null;
        try {
            response = okHttpClient.newCall(request).execute();
            if (response.isSuccessful()) {
                return response.body() != null ? response.body().string() : null;
            }
        } catch (Exception e) {
            log.error("OkHttp3 Put Error >> ex = {}", ExceptionUtils.getStackTrace(e));
        } finally {
            if (response != null) {
                response.close();
            }
        }
        return null;
    }

    /**
     * 调用OkHttp的newCall方法（异步）
     *
     * @param request 请求体
     * @return
     */
    private void execAsyncNewCall(Request request, Callback callback) {
        log.debug("{} {}", request.method(), request.url());
        for (Map.Entry<String, List<String>> header : request.headers().toMultimap().entrySet()) {
            log.debug("{}: {}", header.getKey(), header.getValue());
        }
        try {
            okHttpClient.newCall(request).enqueue(callback);
        } catch (Exception e) {
            log.error("OkHttp3 Put Error >> ex = {}", ExceptionUtils.getStackTrace(e));
        }
    }

    /**
     * get
     *
     * @param url 请求的url
     * @param paramMap 请求的参数，在浏览器？后面的数据，没有可以传null
     * @return
     */
    public String get(String url, Map<String, String> paramMap) {
        Request request = new Request.Builder().url(getUrlStr(url, paramMap))
                //                .addHeader("Connection", "keep-alive")
                .build();
        return execNewCall(request);
    }

    public String getForNull(String url) {
        Request request = new Request.Builder().url(url).build();
        return execNewCall(request);
    }

    public String getByHeader(String url, Headers headers) {
        Request request = new Request.Builder().url(url).headers(headers).build();
        return execNewCall(request);
    }

    /**
     * get 带请求头
     *
     * @param url 请求的url
     * @param paramMap 请求参数，没有可传null
     * @param headerMap 请求头，没有可传null
     * @return
     */
    public String get(String url, Map<String, String> paramMap, Map<String, String> headerMap) {
        if (headerMap == null) {
            return get(url, paramMap);
        }
        Request request = new Request.Builder().url(getUrlStr(url, paramMap)).headers(Headers.of(headerMap)).build();
        return execNewCall(request);
    }

    /**
     * post （同步）
     *
     * @param url 请求的url
     * @param params post form 提交的参数
     * @return
     */
    public String postFormParams(String url, Map<String, String> params) {
        FormBody.Builder builder = new FormBody.Builder();
        //添加参数
        if (params != null && params.keySet().size() > 0) {
            for (String key : params.keySet()) {
                builder.add(key, params.get(key));
            }
        }
        Request request = new Request.Builder().url(url).post(builder.build()).build();
        return execNewCall(request);
    }

    /**
     * Post请求发送JSON数据....{"name":"zhangsan","pwd":"123456"}
     * 参数一：请求Url
     * 参数二：请求的JSON
     */
    public String postJsonParams(String url, String jsonParams) {
        return postJsonParams(url, jsonParams, null);
    }

    /**
     * Post请求发送JSON数据....{"name":"zhangsan","pwd":"123456"}
     * 参数一：请求Url
     * 参数二：请求的JSON
     */
    public String postJsonParams(String url, String jsonParams, Headers headers) {
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonParams);
        Request.Builder request = new Request.Builder();
        request.url(url);
        if (Objects.nonNull(headers)) {
            request.headers(headers);
        }
        request.post(requestBody);
        return execNewCall(request.build());
    }

    /**
     * Post请求发送xml数据....
     * 参数一：请求Url
     * 参数二：请求的xmlString
     */
    public String postXmlParams(String url, String xml) {
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/xml; charset=utf-8"), xml);
        Request request = new Request.Builder().url(url).post(requestBody).build();
        return execNewCall(request);
    }

    public void asyncPostJsonParams(String url, String jsonParams) {
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonParams);
        Request request = new Request.Builder().url(url).post(requestBody).build();
        execAsyncNewCall(request, new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                log.debug("onFailure: " + e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                log.debug(response.protocol() + " " + response.code() + " " + response.message());
                Headers headers = response.headers();
                for (int i = 0; i < headers.size(); i++) {
                    log.debug(headers.name(i) + ":" + headers.value(i));
                }
                log.debug("onResponse: " + response.body().string());
            }
        });
    }

    public void asyncPostJsonParams(String url, String jsonParams, Callback callback) {
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonParams);
        Request request = new Request.Builder().url(url).post(requestBody).build();
        execAsyncNewCall(request, callback);
    }

    public String upload(String url, byte[] bytes, String fileName) throws Exception {
        RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
                .addFormDataPart("file", fileName, RequestBody.create(MediaType.parse("application/pdf"), bytes))
                .build();
        Request request = new Request.Builder()
                //.header("Authorization", "Client-ID " + UUID.randomUUID())
                .url(url).post(requestBody).build();
        Response response = okHttpClient.newCall(request).execute();
        if (!response.isSuccessful())
            throw new IOException("Unexpected code " + response);

        return response.body().string();
    }
}
